Analyzing Playstyles and Mechanics of Counter Strike
Background: Counter Strike: Global Offensive is a video game in which teams of 5 compete to win matches that consist of 30 individual rounds. The players are split into two teams of “Terrorist” and “Counter-Terrorist”, in which the Terrorist team can win by detonating a bomb at one of two ‘sites’ or eliminating all of the other team, and Counter-Terrorists can win by defusing the terrorists’ bomb or eliminating all of the other team. Games are split into two halves of 15 rounds each, and each team spends one half as the Terrorists and one half as the Counter-Terrorists.
At the end of each round, each player is rewarded a certain amount of in-game money based on their actions in the round (kills, planting the bomb, etc), the winning team of the round, and the results of the preceding rounds (for example: losing your fifth round in a row will grant the losing team \$3400 per player, where as losing only one round will grant each player just \$1400). This money is then used in the next round to purchase equipment (weapons, grenades, armor, etc). As the game progresses, teams can afford stronger weapons and more grenades/armor. Each teams buying strategies at the beginning of a round are categorized as follows:
-Pistol Round - The first round of each half where each team has only $800 per player and can not afford to buy anything stronger than a pistol or grenades.
-Eco Round/Semi-Eco Round - When one team does not have enough money for a full buy and chooses to save most or all of their cash in the hopes of going into the following rounds with a better economy in which they can buy full loadouts.
-Force Buy - When one team does not have enough money for a full buy and chooses to spend it all on whatever they can afford. This will usually consist of them buying only the cheaper, less powerful rifles or SMGs. T
-Full Buy/Normal Buy - When a team has enough money to afford the ‘best’ weapons and does not go out of their way to save for future rounds.
This analysis will look at some trends within Counter Strike matches as well as analyze the effects of using different weapons at different ranges and how the in-game economy and buying patterns influence the outcome of rounds/games.
In implemented this data analysis, we used Python 3 with some important libraries such as pandas (for managing data), numpy (for working with numbers), and matplotlib (for representing data visually).
#++MODULES
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.misc import imread
import warnings
warnings.filterwarnings('ignore')
import random
import sklearn
from sklearn import linear_model
import math
from statistics import mean
import seaborn as sns
import statsmodels.api as sm
from statsmodels.formula.api import ols
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import KFold, cross_val_score
For this project we are working with Kevin Pei's CS:GO Competitive Matchmaking Data database.
This data set, alongside other things, contains records of over 1000 public matches of various skill levels showing second-by-second recordings of different games with numbers on damage dealt, player positions, grenades thrown, bombs planted, etc. It also contains data on various maps of Counter Strike with their dimensions and rendeings of these maps from a birds' eye view. This will be useful in visualizing positional data. Specifically, we will be using map_data.csv, mm_grenades_demos.csv, mm_master_demos.csv, and the .png map files.
Firstly, we need to process our data into our local environment. To do this, we'll use pandas to read the data from the csv files and store it as dataframes. This will give us our data in an easily accessable form to aid in our future manipulation and analysis. We will be using pandas throughout this project. For more info, visit pandas documentation.
df = pd.read_csv("data/CSVs/mm_master_demos.csv")
mapdf = pd.read_csv("data/CSVs/map_data.csv")
grenadedf = pd.read_csv("data/CSVs/mm_grenades_demos.csv")
After processing the data, we need to tidy it up by renaming certain columns to be more easily interpretable and dropping other columns that we won't need for future analysis. While tracking variables like armor damage or which site the bomb is planted at could be useful in other projects using this dataset, it is unnecessary for our purposes and simply takes up space.
#rename columns for clarity and drop unnecessary data
df.rename(columns = {'file': 'match_no'}, inplace = 'true')
df.drop(columns=['date', 'tick', 'award', 'vic_side', 'arm_dmg', 'is_bomb_planted', 'bomb_site', 'award', 'att_rank'])
#rename columns for clarity and drop unnecessary data
mapdf.drop(columns = ['ResX', 'ResY'])
mapdf.rename(columns = {'Unnamed: 0': 'map'}, inplace=True)
#rename columns for clarity and drop unnecessary data
grenadedf.drop(columns = ['hitbox', 'ct_eq_val', 't_eq_val', 'is_bomb_planted', 'arm_dmg', 'hp_dmg', 'winner_team', 'winner_side', 'att_rank', 'vic_rank', 'vic_pos_x', 'vic_pos_y', 'round_type', 'ct_eq_val', 't_eq_val', 'round', 'start_seconds', 'vic_id', 'vic_side', 'bomb_site', 'att_team', 'vic_team', 'end_seconds', 'att_id', 'seconds'], inplace = True)
Next, we implement a new column that tracks whether the team with more expensive equipment in a certain round is the winner of that round. This will be useful in analysis later.
#check to see whether the winning team of each
#round was also the team who spent the most $
#save to new column
df['higher_wins'] = False
for index, row in df.iterrows():
if (((row['winner_side'] == 'CounterTerrorist') and (row['ct_eq_val'] > row['t_eq_val'])) or ((row['winner_side'] == 'Terrorist') and (row['t_eq_val'] > row['ct_eq_val']))):
df.at[index, 'higher_wins'] = True
To begin with our analysis, we try to uncover certain trends within the data that could reveal insights into the behaviors of players within Counter Strike. Thanks to the comprehensive map and player positioning data, we can visualize some of our data in a way that is very easily interpretable.
For our first positional analysis, we will simply visualize all damage dealt and received over the course of one random match from our dataset. The blue circles will represent someone dealing damage, and the red circles will represent someone recieving damage. Below is a representation of that on the fan-favorite map, Dust 2.
To do this, we will first load the background map as provided by the dataset. Next, we will pick a random game from the dataframe and create a new dataframe that contains only the incidents of damage from that match. Next we will use matplotlib to create a scatter plot from the position information included in our new dataframe and the map-specific coordinate boundaries from the map dataset.
We will be using matplotlib.pyplot throughout this project for visual representations of data. matplotlib.pyplot documentation
map_input = "de_dust2"
bg = imread(f'data/maps/{map_input}.png')
#select random game from dust2 and create matching dataframe
match = random.choice(list(df.loc[(df.map == map_input)].groupby('match_no').groups.keys()))
plot_df = df.loc[(df.match_no == match)]
#find map coordinate data from map dataframe
map_data = mapdf.loc[mapdf.map == map_input]
coords = [map_data.StartX.sum(), map_data.EndX.sum(), map_data.StartY.sum(), map_data.EndY.sum()]
#create matplotplib graph and overlay over background image
plt.figure(figsize=(15, 15))
plt.imshow(bg, aspect = 'equal', interpolation = 'none', extent = coords)
plt.scatter(plot_df['att_pos_x'] + 25, plot_df['att_pos_y'], alpha=.5, c='blue')
plt.scatter(plot_df['vic_pos_x'] + 25, plot_df['vic_pos_y'], alpha=.5, c='red')
plt.title(f'Damage Dealt', fontsize=30)
plt.axis('off')
plt.show()
From this image, we can get an idea of where players are most often congregating and dealing/receiving damage to one another. At the top and bottom centers, we can see where the Terrorist spawn (bottom in green) has a line of sight across the middle of the map and through a doorway next to Counter-Terrorist spawn. This is a common area for enemy engagement at the beggingings of rounds, and that is reflected in the data. We also can see that bomb sitses and choke points between the two teams' spawn have the highest density of player interaction, whereas areas like the bottom left have very little data, as these areas do not often see any fighting.
Next, we can look at a more generalized representation of player movements and interactions in a specific map. In this case, we are looking at damage dealt (represented in red) by Terrorists (represented in blue) in the map Cache over the course of hundreds of matches (237 to be exact).
map_input = "de_cache"
bg = imread(f'data/maps/{map_input}.png')
#select all games from Cache and create matching dataframe
plot_df = df.loc[(df.map == map_input) & (df.att_side == 'Terrorist')]
#find map coordinate data from map dataframe
map_data = mapdf.loc[mapdf.map == map_input]
coords = [map_data.StartX.sum(), map_data.EndX.sum(), map_data.StartY.sum(), map_data.EndY.sum()]
#create matplotplib graph and overlay over background image
plt.figure(figsize=(15, 15))
plt.imshow(bg, aspect = 'equal', interpolation = 'none', extent = coords)
plt.scatter(plot_df['att_pos_x'] + 25, plot_df['att_pos_y'], alpha=.01, c='blue')
plt.scatter(plot_df['vic_pos_x'] + 25, plot_df['vic_pos_y'], alpha=.01, c='red')
plt.title(f'Damage Dealt by Terrorists', fontsize=30)
plt.axis('off')
plt.show()
From this, we can see clear trends in where the terrorists and counter-terrorists are attacking each other. The distribution of terrorists on the right and counter-terrorists on the left (where they spawn, respectively) is very clear, and commong Counter-Terrorist defensive positions appear as large red masses, as this is where they are being attacked by terrorists.
We can now look again at the exact same data, but this time restricted to only players using the 'AWP', a powerful sniper.
map_input = "de_cache"
bg = imread(f'data/maps/{map_input}.png')
#select all games from cache with AWP and create matching dataframe
plot_df = df.loc[(df.map == map_input) & (df.att_side == 'Terrorist') & (df.wp == 'AWP')]
#find map coordinate data from map dataframe
map_data = mapdf.loc[mapdf.map == map_input]
coords = [map_data.StartX.sum(), map_data.EndX.sum(), map_data.StartY.sum(), map_data.EndY.sum()]
#create matplotplib graph and overlay over background image
plt.figure(figsize=(15, 15))
plt.imshow(bg, aspect = 'equal', interpolation = 'none', extent = coords)
plt.scatter(plot_df['att_pos_x'] + 25, plot_df['att_pos_y'], alpha=.1, c='blue')
plt.scatter(plot_df['vic_pos_x'] + 25, plot_df['vic_pos_y'], alpha=.1, c='red')
plt.title(f'Damage Dealt by Terrorists with AWP', fontsize=30)
plt.axis('off')
plt.show()
We can see that the range of engagement is much longer here than in our previous map, with the distance between clusters of players dealing/receiving damage being much greater. Similar yet opposite trends can be seen when selecting for short range weapons like shotguns.
Next, we can use a similar positional analysis technique to analyze how players use their grenades within a match, specifically how they use smoke grenades. Smoke grenades provide an opaque wall of smoke that Terrorists will use to block Counter-Terrorist sightlines when attacking and Counter-Terrorists will use to prevent their enemies from moving into a bombsite. The thrower of each smoke grenade is represented in red, while the landing spot of each smoke grenade is represented in blue.
map_input = "de_mirage"
#create plot dataframe
plot_df = grenadedf.loc[(grenadedf.nade == 'Smoke') & (grenadedf.map == map_input) & (grenadedf.att_side == 'Terrorist')]
bg = imread(f'data/maps/{map_input}.png')
#fetch map data
map_data = mapdf.loc[mapdf.map == map_input]
coords = [map_data.StartX.sum(), map_data.EndX.sum(), map_data.StartY.sum(), map_data.EndY.sum()]
#create plot and display background
plt.figure(figsize=(15, 15))
plt.imshow(bg, aspect = 'equal', interpolation = 'none', extent = coords)
plt.scatter(plot_df['att_pos_x'] + 25, plot_df['att_pos_y'], alpha=.1, c='blue')
plt.scatter(plot_df['nade_land_x'] + 25, plot_df['nade_land_y'], alpha=.11, c='red')
plt.title(f'Terrorist Smoke Grenades', fontsize=20)
plt.axis('off')
plt.show()
#create plot dataframe
plot_df = grenadedf.loc[(grenadedf.nade == 'Smoke') & (grenadedf.map == map_input) & (grenadedf.att_side == 'CounterTerrorist')]
bg = imread(f'data/maps/{map_input}.png')
#fetch map data
map_data = mapdf.loc[mapdf.map == map_input]
coords = [map_data.StartX.sum(), map_data.EndX.sum(), map_data.StartY.sum(), map_data.EndY.sum()]
#create plot and display background
plt.figure(figsize=(15, 15))
plt.imshow(bg, aspect = 'equal', interpolation = 'none', extent = coords)
plt.scatter(plot_df['att_pos_x'] + 25, plot_df['att_pos_y'], alpha=.1, c='blue')
plt.scatter(plot_df['nade_land_x'] + 25, plot_df['nade_land_y'], alpha=.11, c='red')
plt.title(f'Counter-Terrorist Smoke Grenades', fontsize=20)
plt.axis('off')
plt.show()
As we can see, the terrorists (right side spawn) have a tendency their grenades from between their spawn A site (bottom center) and cut off the line of sight between their attack position and Counter-Terrorist spawn. A similar trend can be seen at site B (top left), though less well defined.
The Counter Terrorists, on the other hand, tend to throw the majority of their smokes at the choke points where the Terrorists pass through while trying to reach the bomb sites.
While the clusters are noticeable, it is also clear that the smoke grenades do not all land in their exact intended spot. We can select for only matches where the average player rank was above a certain threshold and see how that changes the outcome.
map_input = "de_mirage"
#create plot dataframe
plot_df = grenadedf.loc[(grenadedf.nade == 'Smoke') & (grenadedf.map == map_input) & (grenadedf.att_side == 'Terrorist') & (grenadedf.avg_match_rank > 15)]
bg = imread(f'data/maps/{map_input}.png')
#fetch map data
map_data = mapdf.loc[mapdf.map == map_input]
coords = [map_data.StartX.sum(), map_data.EndX.sum(), map_data.StartY.sum(), map_data.EndY.sum()]
#create plot and display background
plt.figure(figsize=(15, 15))
plt.imshow(bg, aspect = 'equal', interpolation = 'none', extent = coords)
plt.scatter(plot_df['att_pos_x'] + 25, plot_df['att_pos_y'], alpha=.5, c='blue')
plt.scatter(plot_df['nade_land_x'] + 25, plot_df['nade_land_y'], alpha=.5, c='red')
plt.title(f'Terrorist Smoke Grenades', fontsize=20)
plt.axis('off')
plt.show()
#create plot dataframe
plot_df = grenadedf.loc[(grenadedf.nade == 'Smoke') & (grenadedf.map == map_input) & (grenadedf.att_side == 'CounterTerrorist') & (grenadedf.avg_match_rank > 15)]
bg = imread(f'data/maps/{map_input}.png')
#fetch map data
map_data = mapdf.loc[mapdf.map == map_input]
coords = [map_data.StartX.sum(), map_data.EndX.sum(), map_data.StartY.sum(), map_data.EndY.sum()]
#create plot and display background
plt.figure(figsize=(15, 15))
plt.imshow(bg, aspect = 'equal', interpolation = 'none', extent = coords)
plt.scatter(plot_df['att_pos_x'] + 25, plot_df['att_pos_y'], alpha=.5, c='blue')
plt.scatter(plot_df['nade_land_x'] + 25, plot_df['nade_land_y'], alpha=.5, c='red')
plt.title(f'Counter-Terrorist Smoke Grenades', fontsize=20)
plt.axis('off')
plt.show()
Here we can see the same data as before, but with much less variance in the positions. It is clear that these higher-ranked players have more experience and are more consistent in throwing accurate smoke grenades. We can also see that these higher-rank players appear to not use smoke grenades when attacking B very often relative to the general populace. Presenting the data like this can be valuable in finding insight into strategies that you may not be aware of.
Analyzing the relation of economy to round win-rate
Next, we analyzed how a teams' spending habits predict whether or not they will win a round. Obviously, our hypothesis was that spending more money than the other team would result in increased chances of winning the round. To check this, we needed to group our data into individual rounds and look at whether the team who spent more in that round also won the round. To do this, we used our 'higher_wins' column that we created earlier. We then calculated the differences in team spending into increments of $1000 and found the winrate of the higher spending teams within these increments. Finally, we used the matplotlib data visualization library, seaborn, to plot this in a bar graph. seaborn documentation
grouped = df.groupby(['match_no', 'round'])
#group data into individual rounds
econdf = pd.DataFrame()
count = 0
for name, group in grouped:
econdf = pd.concat([econdf, group.head(1)], ignore_index = True)
count += 1
if count > 25000:
break
#create new dataframe from grouped data for plotting purposes
econdf = econdf[['ct_eq_val', 't_eq_val', 'higher_wins']]
for index, row in econdf.iterrows():
econdf.at[index, 'diff'] = abs(row['ct_eq_val'] - row['t_eq_val'])
econ = econdf.groupby(['diff'])['higher_wins'].mean().to_frame()
for index, row in econgrouped.iterrows():
econ.at[index, 'diffc'] = index
#take avg of winning proabibility in increments of $1000
problist = []
for i in range(0, 30000, 1000):
avg = econ.loc[(econ['diffc'] > i) & (econ['diffc'] <= i + 1000)]
problist.append(avg['higher_wins'].mean())
X = []
Y = []
#create x and y variables from earlier data
for i in range(0, 30):
X.append(i)
for prob in problist:
Y.append(prob)
#create plot
plt.figure(figsize=(12,6))
p = sns.barplot(x=X, y = Y, orient='v')
p.set_xlabel("Difference in money spent (in thousands of dollars)", fontsize = 15)
p.set_ylabel("Likelihood of winning round", fontsize = 15)
p.set_title('Probability of team with higher value equipment winning round', fontsize = 20)
As we can see, the likelihood of winning a round clearly correlates with how much more money a team spends than their opponents. While the probability of winning hovers around 50% when the difference is <$5000, it increases to nearly 100% when the difference in spending approaches $30,000. This just demonstrates how important the economy in Counter Strike is and how difficult it is to win a round against a full-buying team when you are on an eco or force buy round. While more analysis could be done of the economic impact on round wins, we would find the same trends.
groups = df.groupby(['match_no', 'round', 'vic_id'])
netWeaponDistances = {}
netWeaponInstances = {}
netWeaponTypeDistances = {}
netWeaponTypeInstances = {}
def distance(x1,y1,x2,y2):
return math.sqrt( ((int(x1)-int(x2))**2)+((int(y1)-int(y2))**2) )
def meanDistance(AttackXCords, AttackYCords, VictimXCords, VictimYCords):
totalDistance = 0.0
totalHits = 0
merged_list = tuple(zip(AttackXCords, AttackYCords,VictimXCords,VictimYCords))
for tup in merged_list:
x1 = tup[0]
y1 = tup[1]
x2 = tup[2]
y2 = tup[3]
totalDistance = totalDistance + distance(x1,y1,x2,y2)
totalHits = totalHits + 1
return totalDistance/totalHits
#roundGroups[hp_dmg].sum()
i = 0
for name, group in groups:
totalDamage = group['hp_dmg'].sum()
result = True
weaponUsed = group['wp']
weaponTypeUsed = group['wp_type']
numIters = 0
weaponType = ''
for elt in weaponTypeUsed:
if numIters == 0:
weaponType = elt
elif weaponType != elt:
result = False
break
numIters = 1
numIters = 0
weapon = ''
for elt in weaponUsed:
if numIters == 0:
weapon = elt
elif weapon != elt:
result = False
break
numIters = 1
if weapon == 'Unknown' or weaponType == 'Equipment' or weaponType == 'Grenade':
continue
if (result):
if totalDamage == 100:
AX = group['att_pos_x']
AY = group['att_pos_y']
VX = group['vic_pos_x']
VY = group['vic_pos_y']
try:
netWeaponDistances[weapon] = netWeaponDistances[weapon] + meanDistance(AX,AY,VX,VY)
except KeyError:
netWeaponDistances[weapon] = meanDistance(AX,AY,VX,VY)
try:
netWeaponInstances[weapon] = netWeaponInstances[weapon] + 1
except KeyError:
netWeaponInstances[weapon] = 1
try:
netWeaponTypeDistances[weaponType] = netWeaponTypeDistances[weaponType] + meanDistance(AX,AY,VX,VY)
except KeyError:
netWeaponTypeDistances[weaponType] = meanDistance(AX,AY,VX,VY)
try:
netWeaponTypeInstances[weaponType] = netWeaponTypeInstances[weaponType] + 1
except KeyError:
netWeaponTypeInstances[weaponType] = 1
#i = i + 1
if i >= 10:
break
finalWeaponDistances = {}
for key in netWeaponDistances:
finalWeaponDistances[key] = netWeaponDistances[key] / netWeaponInstances[key]
finalWeaponTypeDistances = {}
for key in netWeaponTypeDistances:
finalWeaponTypeDistances[key] = netWeaponTypeDistances[key] / netWeaponTypeInstances[key]
sortedWeaponDistances = {k: v for k, v in sorted(finalWeaponDistances.items(), key=lambda item: item[1])}
sortedWeaponTypeDistances = {k: v for k, v in sorted(finalWeaponTypeDistances.items(), key=lambda item: item[1])}
#print(sortedWeaponDistances)
X = []
Y = []
for weapon in sortedWeaponDistances.keys():
X.append(str(weapon))
for dist in sortedWeaponDistances.values():
Y.append(float(dist))
plt.figure(figsize=(12,6))
plt.xticks(rotation = 80)
sns.barplot(x=X, y = Y, orient='v')
X = []
Y = []
for weaponType in sortedWeaponTypeDistances.keys():
X.append(str(weaponType))
for dist in sortedWeaponTypeDistances.values():
Y.append(float(dist))
plt.figure(figsize=(12,6))
plt.xticks(rotation = 80)
sns.barplot(x=X, y = Y, orient='v')
# could do some sort of percent chance of winnign a gunfight given the distance between the two players and their weapons!
roundGroups = df.groupby(['match_no', 'round'])
numRounds = 0
numRows = 0
weaponDistanceHealth = {} # weapon -> list of (distance, health) tuples
# map each attacker for each round with their weapon that round, then search the dict for their ID and if they are there' retrieve their gun
# go through each value of attacker, see if they killed the other person (2 people can show up as attackers, but just one of them will win)
weaponKills = {} # 'tuple' (<weapon of attacker used to kill>, <weapon of victim who died>) -> (number of times this happened, TOTAL DISTANCE) , we will eventually compare the inverse to find a win average and calculate mean distance of a win
rowCount = 0
#mappedEncounters = {}
# we need a mapping of attacker -> victims they attacked and also a mapping victims -> who they got attacked by
for name, round in roundGroups:
roundDF = pd.DataFrame(round)
# for each round, make a dict mapping player ID to their weapon that round
playerWeapons = {} # playerID -> weapon used this round
# if the encounter resulted in 100 damage, then the enemy was killed, log it as a kill from one weapon vs another and also the distance the players were
#weaponKills = {} # weapon used to kill -> weapon of victim
# have to keep track of each victim's health throughout the whole round. If victim health reaches <=0, then map the attacker's weapon to the victim's weapon if it exists.
# If the victim doesn't yet exist in the mapping, then subtract the damage done from 100
playerHealth = {} # playerID -> health (0-100)
playerWeaponClasses = {}
#encounters = round.groupby(['att_id','vic_id'])
for index, row in roundDF.iterrows():
rowCount = rowCount + 1
attacker = row['att_id']
victim = row['vic_id']
attackWeapon = row['wp']
damage = row['hp_dmg']
weaponClass = row['wp_type']
AX = row['att_pos_x']
AY = row['att_pos_y']
VX = row['vic_pos_x']
VY = row['vic_pos_y']
# removes non-weapons from consideration
if attackWeapon == 'Unknown' or weaponClass == 'Equipment' or weaponClass == 'Grenade':
continue
# map each player's weapon from the round
playerWeapons[attacker] = attackWeapon
playerWeaponClasses[attacker] = weaponClass
# map each player's health
try:
playerHealth[victim] = playerHealth[victim] - damage
except KeyError:
playerHealth[victim] = 100 - damage
if attacker not in playerHealth:
playerHealth[attacker] = 100
# track health of attacker
'''
if attackWeapon in weaponDistanceHealth:
list_ = weaponDistanceHealth[attackWeapon]
list_.append((distance(AX, AY, VX, VY),playerHealth[attacker]))
weaponDistanceHealth[attackWeapon] = list_
else:
list_ = []
list_.append((distance(AX, AY, VX, VY),playerHealth[attacker]))
weaponDistanceHealth[attackWeapon] = list_
'''
# check if victim is dead
if playerHealth[victim] <= 0:
# check if victim's weapon is logged
try:
victimWeapon = playerWeapons[victim]
victimWeaponClass = playerWeaponClasses[victim]
try:
try:
list_ = weaponDistanceHealth[weaponClass]
list_.append((distance(AX, AY, VX, VY),playerHealth[attacker]))
weaponDistanceHealth[weaponClass] = list_
except (KeyError, AttributeError):
list2_ = []
list2_.append((distance(AX, AY, VX, VY),playerHealth[attacker]))
weaponDistanceHealth[weaponClass] = list2_
try:
list_ = weaponDistanceHealth[victimWeaponClass]
list_.append((distance(AX, AY, VX, VY),playerHealth[victim]))
weaponDistanceHealth[victimWeaponClass] = list_
except (KeyError, AttributeError):
list2_ = []
list2_.append((distance(AX, AY, VX, VY),playerHealth[victim]))
weaponDistanceHealth[victimWeaponClass] = list2_
(occurences, totalDistance) = weaponKills[(attackWeapon, victimWeapon)]
weaponKills[(attackWeapon, victimWeapon)] = (occurences + 1, totalDistance + distance(AX, AY, VX, VY))
except KeyError:
weaponKills[(attackWeapon, victimWeapon)] = (1, distance(AX, AY, VX, VY))
except KeyError:
pass
#print(row['c1'], row['c2'])
#print(row)
#numRows = numRows + 1
if numRows >= 200:
break
numRows = 0
#numRounds = numRounds + 1
if numRounds >= 800:
break
def f(tup):
(oc, dist) = tup
return dist/oc
d = {'A': 0, 'B': 1, 'C': 2}
#print(weaponKills)
newDict = {k: f(v) for k, v in weaponKills.items()}
#print(newDict)
doneMatchups = {}
for item in newDict.items():
(key_, distance_) = item
(weapon1, weapon2) = key_
if key_ in doneMatchups or (weapon2, weapon1) in doneMatchups:
continue
else:
doneMatchups[key_] = 1
try:
weapon2WinDistance = newDict[(weapon2, weapon1)]
except KeyError:
weapon2WinDistance = -1
#if weapon2WinDistance != -1:
#print("Matchup between " + str(weapon1) + " and " + str(weapon2) + "\n")
#print(str(weapon1) + " wins at " + str(distance_) + "\n" + str(weapon2) + " wins at "+ str(weapon2WinDistance) + "\n\n\n")
#else:
#print("Matchup between " + str(weapon1) + " and " + str(weapon2) + "\n")
#print(str(weapon1) + " wins at " + str(distance_) + "\n" + str(weapon2) + " wins at UNKNOWN"+ "\n\n\n")
#print(weaponKills)
print(rowCount)
#print(weaponDistanceHealth)
# weapon -> list of (distance, health) tuples
#distHealth['<= 300']
weaponList = ['SMG', 'Heavy', 'Pistol', 'Rifle', 'Sniper']
# weapon -> list of (distance, health) tuples
for weapon in weaponList:
X = []
Y = []
distHealth = {}
for tup in weaponDistanceHealth[weapon]:
(distance__, health__) = tup
if (distance__ <= 300):
X.append('0 - 300')
elif (distance__ <= 600):
X.append('300 - 600')
elif (distance__ <= 900):
X.append('600 - 900')
elif (distance__ <= 1200):
X.append('900 - 1200')
elif (distance__ <= 1500):
X.append('1200 - 1500')
elif (distance__ <= 1800):
X.append('1500 - 1800')
elif (distance__ <= 2100):
X.append('1800 - 2100')
elif (distance__ >= 2100):
X.append('>= 2100')
plt.figure(figsize=(12,6))
plt.xticks(rotation = 80)
sns.histplot(X).set(title=weapon)
def roundup(x):
return int(math.ceil(x / 100.0)) * 100
weaponList = ['SMG', 'Heavy', 'Pistol', 'Rifle', 'Sniper']
# weapon -> list of (distance, health) tuples
for weapon in weaponList:
X = []
Y = []
distHealth = {}
#distHealth['<= 300']
for tup in weaponDistanceHealth[weapon]:
(distance__, health__) = tup
if health__ < 0:
continue
roundDist = roundup(distance__)
if distance__ >= 2500:
continue
try:
list_ = distHealth[roundDist]
list_.append(health__)
distHealth[roundDist] = list_
except (KeyError, AttributeError):
list2_ = []
list2_.append(health__)
distHealth[roundDist] = list2_
#print(distHealth)
for tup in distHealth.items():
(DISTANCE, HEALTH) = tup
X.append(DISTANCE)
Y.append(mean(HEALTH))
plt.figure(figsize=(12,6))
plt.xticks(rotation = 80)
sns.scatterplot(X,Y).set(title=weapon)
#%%shell
#jupyter nbconvert --to html /content/320_Final_Project_Code_Durkin_and_Pool.ipynb